package com.jintian.smart.kernel.common.expr;

import com.jintian.smart.kernel.core.expr.IExpressionCalc;
import com.jintian.smart.kernel.core.expr.IFunctionRepository;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;

public class SpelExpressionCalc implements IExpressionCalc, InitializingBean {
    @Autowired(required = false)
    private IFunctionRepository IFunctionRepository;

    private Map<String, Method> functionMethods;

    private ExpressionParser parser;

    private ParserContext parserContext;

    @Override
    public void setFunctionRepository(IFunctionRepository IFunctionRepository) {
        this.IFunctionRepository = IFunctionRepository;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        IFunctionRepository repository = this.IFunctionRepository;
        if (repository == null) {
            repository = new DefaultFunctionRepository();
        }

        this.functionMethods = repository.getNamedMethod();
        if (this.parser == null) {
            this.parser = new SpelExpressionParser();
        }

    }

    @Override
    public void reloadFunctions() {
        try {
            this.afterPropertiesSet();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object calculate(String expr, Map<String, Object> variants) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariables(this.getLocalContext());
        if (variants != null && variants.containsKey("_root")) {
            context.setRootObject(variants.remove("_root"));
        }
        context.setVariables(variants);
        this.functionMethods.forEach(context::registerFunction);
        Expression expression = parser.parseExpression(expr, parserContext);
        return expression.getValue(context);
    }

    public Map<String, Method> getFunctionMethods() {
        return Collections.unmodifiableMap(this.functionMethods);
    }

}
